#include <QDebug>
#include <QList>
#include <QListIterator>
#include <QPushButton>
#include <QFileDialog>
#include <QXmlStreamWriter>
#include <QIcon>
#include <QCommonStyle>

#include "texttablemodel.h"
#include "dialog.h"
#include "ui_dialog.h"
#include "edge.h"

Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
    QCommonStyle style;
    QPushButton *saveButton = new QPushButton(style.standardIcon(QStyle::SP_DialogSaveButton), "Сохранить в Excel", this);
    QPushButton *okButton = new QPushButton(style.standardIcon(QStyle::SP_DialogOkButton), "Закрыть", this);
    ui->buttonBox->addButton(saveButton, QDialogButtonBox::ActionRole);
    ui->buttonBox->addButton(okButton, QDialogButtonBox::AcceptRole);
    ui->tableView->setModel(new TextTableModel(this));
}

Dialog::~Dialog()
{
    delete ui;
}

void Dialog::showMatrixCircuit(const QList<Node*> &nodeList)
{
    Node *bal;
    int columns = 0;

    // Поиск балансирующего узла
    foreach (Node *node, nodeList) {
        if (node->highlighted()) {
            bal = node;
        }
        QList<Edge*> edgeList = node->edges();

        // и количества ветвей
        foreach (Edge *edge, edgeList) {
            columns = qMax(columns, edge->number());
        }
    }

    StringTable table(0, columns);
    Node *node = bal;
    QList<QListIterator<Edge*> > q;
    if (!bal->edges().isEmpty()) {
        q << QListIterator<Edge*>(bal->edges());
    }

    while(!q.isEmpty()) {
        QListIterator<Edge*> it(q.last());
        Edge *edge = it.peekNext();

        node = edge->oppositeNode(node); // Конец ветви

        if (node == bal && q.size() > 2) { // Контур замкнулся
            QStringList row;
            for (int i = 0; i < columns; i++) //Заполнение строки нолями
                row << "0";

            Node *node2 = bal;
            foreach (QListIterator<Edge*> it2, q) { // Заполнение строки направлениями
                Edge *edge2 = it2.peekNext();
                row[edge2->number() - 1] = node2->number() < edge2->oppositeNode(node2)->number() ? "-1" : "1";
                node2 = edge2->oppositeNode(node2);
            }
            table.appendRow(row);
            node = edge->oppositeNode(node);
            q.removeLast();
            it.next();
            while(!it.hasNext() && node != bal) {
                it = q.last();
                node = it.next()->oppositeNode(node);
                q.removeLast();
            }

            if (it.hasNext())
                q << it;
        }
        else { // Идти дальше
            Node *node2 = bal;
            bool b = true;
            foreach (QListIterator<Edge*> it2, q) { // Проверка петли
                Edge *edge2 = it2.peekNext();
                if (node2 == node) { // Такой контур нам не нужен!
                    node = edge->oppositeNode(node);
                    q.removeLast();
                    it.next();
                    while(!it.hasNext() && node != bal) {
                        it = q.last();
                        node = it.next()->oppositeNode(node);
                        q.removeLast();
                    }

                    if (it.hasNext())
                        q << it;
                    b = false;
                    break;
                }
                node2 = edge2->oppositeNode(node2);
            }
            if (b) {
                q << QListIterator<Edge*>(node->edges());
            }
        }
    }

    qobject_cast<TextTableModel*>(ui->tableView->model())->setText(table);
    setWindowTitle("Матрица контуров");

    QStringList labels;
    for (int i = 0; i < columns; i++) {
        ui->tableView->setColumnWidth(i, 40);
        labels << QString::number(i + 1);
    }
    qobject_cast<TextTableModel*>(ui->tableView->model())->setHorizontalHeaderLabels(labels);
    show();
}

void Dialog::showMatrixBranches(const QList<Node*> &nodeList)
{
    StringTable table(nodeList.size(), nodeList.size());

    // Заполнение матрицы нулями
    for (int i = 0; i < nodeList.size(); i++) {
        for (int j = 0; j < nodeList.size(); j++) {
            table.setCell(i, j, "0");
        }
    }

    // Запись в матрицу номеров соединящих ветвей
    foreach (Node *node, nodeList) {
        foreach (Edge *edge, node->outEdges()) {
            table.setCell(node->number() - 1, edge->destNode()->number() - 1, QString::number(edge->number()));
            table.setCell(edge->destNode()->number() - 1, node->number() - 1, QString::number(edge->number()));
        }
    }


    qobject_cast<TextTableModel*>(ui->tableView->model())->setText(table);
    setWindowTitle("Матрица ветвей");

    QStringList labels;
    for (int i = 0; i < nodeList.size(); i++) {
        ui->tableView->setColumnWidth(i, 40);
        labels << QString::number(i + 1);
    }
    qobject_cast<TextTableModel*>(ui->tableView->model())->setHorizontalHeaderLabels(labels);
    show();
}

void Dialog::showMatrixPaths(const QList<Node*> &nodeList)
{
    Node *bal;
    int columns = 0;

    // Поиск балансирующего узла
    foreach (Node *node, nodeList) {
        if (node->highlighted()) {
            bal = node;
        }
        QList<Edge*> edgeList = node->edges();

        // и количества ветвей
        foreach (Edge *edge, edgeList) {
            columns = qMax(columns, edge->number());
        }
    }

    StringTable table(0, columns);

    foreach (Node *examineNode, nodeList) {
        if (examineNode == bal)
            continue;

        Node *node = bal;
        QList<QListIterator<Edge*> > q;
        if (!bal->edges().isEmpty()) {
            q << QListIterator<Edge*>(bal->edges());
        }
        
        while(!q.isEmpty()) {
            QListIterator<Edge*> it(q.last());
            Edge *edge = it.peekNext();

            node = edge->oppositeNode(node); // Конец ветви

            if (node == examineNode) { // Контур замкнулся
                QStringList row;
                for (int i = 0; i < columns; i++) //Заполнение строки нолями
                    row << "0";

                Node *node2 = bal;
                foreach (QListIterator<Edge*> it2, q) { // Заполнение строки направлениями
                    Edge *edge2 = it2.peekNext();
                    row[edge2->number() - 1] = node2->number() < edge2->oppositeNode(node2)->number() ? "-1" : "1";
                    node2 = edge2->oppositeNode(node2);
                }
                table.appendRow(row);
                node = edge->oppositeNode(node);
                q.removeLast();
                it.next();
                while(!it.hasNext() && node != bal) {
                    it = q.last();
                    node = it.next()->oppositeNode(node);
                    q.removeLast();
                }

                if (it.hasNext())
                    q << it;
            }
            else { // Идти дальше
                Node *node2 = bal;
                bool b = true;
                foreach (QListIterator<Edge*> it2, q) { // Проверка петли
                    Edge *edge2 = it2.peekNext();
                    if (node2 == node) { // Такой контур нам не нужен!
                        node = edge->oppositeNode(node);
                        q.removeLast();
                        it.next();
                        while(!it.hasNext() && node != bal) {
                            it = q.last();
                            node = it.next()->oppositeNode(node);
                            q.removeLast();
                        }

                        if (it.hasNext())
                            q << it;
                        b = false;
                        break;
                    }
                    node2 = edge2->oppositeNode(node2);
                }
                if (b) {
                    q << QListIterator<Edge*>(node->edges());
                }
            }
        }
    }

    qobject_cast<TextTableModel*>(ui->tableView->model())->setText(table);
    setWindowTitle("Матрица путей");

    QStringList labels;
    for (int i = 0; i < columns; i++) {
        ui->tableView->setColumnWidth(i, 40);
        labels << QString::number(i + 1);
    }
    qobject_cast<TextTableModel*>(ui->tableView->model())->setHorizontalHeaderLabels(labels);
    show();
}


void Dialog::showCommentMatrix(const QList<Node*> &nodeList)
{
    Node *bal;

    // Поиск балансирующего узла
    foreach (Node *node, nodeList) {
        if (node->highlighted()) {
            bal = node;
        }
    }

    StringTable table(1, nodeList.size());
    for (int i = 0; i < nodeList.size(); i++) { // Заполнение матрицы нулями
        table.setCell(0, i, "0");
    }


    foreach (Node *examineNode, nodeList) {
        if (examineNode == bal)
            continue;
        
        int numberPaths = 0;
        
        Node *node = bal;
        QList<QListIterator<Edge*> > q;
        if (!bal->edges().isEmpty()) {
            q << QListIterator<Edge*>(bal->edges());
        }

        while(!q.isEmpty()) {
            QListIterator<Edge*> it(q.last());
            Edge *edge = it.peekNext();

            node = edge->oppositeNode(node); // Конец ветви

            if (node == examineNode) { // Контур замкнулся
                numberPaths++;
                node = edge->oppositeNode(node);
                q.removeLast();
                it.next();
                while(!it.hasNext() && node != bal) {
                    it = q.last();
                    node = it.next()->oppositeNode(node);
                    q.removeLast();
                }

                if (it.hasNext())
                    q << it;
            }
            else { // Идти дальше
                Node *node2 = bal;
                bool b = true;
                foreach (QListIterator<Edge*> it2, q) { // Проверка петли
                    Edge *edge2 = it2.peekNext();
                    if (node2 == node) { // Такой контур нам не нужен!
                        node = edge->oppositeNode(node);
                        q.removeLast();
                        it.next();
                        while(!it.hasNext() && node != bal) {
                            it = q.last();
                            node = it.next()->oppositeNode(node);
                            q.removeLast();
                        }

                        if (it.hasNext())
                            q << it;
                        b = false;
                        break;
                    }
                    node2 = edge2->oppositeNode(node2);
                }
                if (b) {
                    q << QListIterator<Edge*>(node->edges());
                }
            }
        }
        table.setCell(0, examineNode->number() - 1, QString::number(numberPaths));
    }

    qobject_cast<TextTableModel*>(ui->tableView->model())->setText(table);
    setWindowTitle("Комментарий к матрице путей");

    QStringList labels;
    for (int i = 0; i < nodeList.size(); i++) {
        ui->tableView->setColumnWidth(i, 40);
        labels << QString::number(i + 1);
    }
    qobject_cast<TextTableModel*>(ui->tableView->model())->setHorizontalHeaderLabels(labels);
    show();
}

void Dialog::on_buttonBox_clicked(QAbstractButton *button)
{
    if (ui->buttonBox->buttonRole(button) == QDialogButtonBox::ActionRole) {
        QFileDialog dlg(this, "Сохранить матрицу!","", "Файл таблицы (*.xml)");
        dlg.setConfirmOverwrite(true);
        dlg.setAcceptMode(QFileDialog::AcceptSave);
        if(!dlg.exec())
            return;

        QString fileName = dlg.selectedFiles()[0];

        if(!fileName.endsWith(".xml", Qt::CaseInsensitive))
            fileName += ".xml";

        QFile file(fileName);
        file.open(QIODevice::WriteOnly | QIODevice::Text);
        if(!file.isOpen())
            return;

        StringTable table = qobject_cast<TextTableModel*>(ui->tableView->model())->text();

        int n = table.columnCount();
        int m = table.rowCount();

        QXmlStreamWriter stream(&file);
        stream.setAutoFormatting(true);
        stream.writeStartDocument();
        stream.writeProcessingInstruction("mso-application", "progid=\"Excel.Sheet\"");

        stream.writeStartElement("Workbook");
        stream.writeAttribute("xmlns", "urn:schemas-microsoft-com:office:spreadsheet");
        stream.writeAttribute("xmlns:o", "urn:schemas-microsoft-com:office:office");
        stream.writeAttribute("xmlns:x", "urn:schemas-microsoft-com:office:excel");
        stream.writeAttribute("xmlns:ss", "urn:schemas-microsoft-com:office:spreadsheet");
        stream.writeAttribute("xmlns:html", "http://www.w3.org/TR/REC-html40");

        stream.writeStartElement("Styles");
        stream.writeStartElement("Style");
        stream.writeAttribute("ss:ID", "Default");
        stream.writeAttribute("ss:Name", "Normal");
        stream.writeEmptyElement("Alignment");
        stream.writeAttribute("ss:Vertical", "Bottom");
        stream.writeEmptyElement("Borders");
        stream.writeEmptyElement("Font");
        stream.writeAttribute("ss:FontName", "Arial Cyr");
        stream.writeAttribute("x:CharSet", "204");
        stream.writeEmptyElement("Interior");
        stream.writeEmptyElement("NumberFormat");
        stream.writeEmptyElement("Protection");
        stream.writeEndElement(); // Style
        stream.writeEndElement(); // Styles

        stream.writeStartElement("Worksheet");
        stream.writeAttribute("ss:Name", "Лист1");
        stream.writeStartElement("Table");
        stream.writeAttribute("ss:ExpandedColumnCount", QString::number(n));
        stream.writeAttribute("ss:ExpandedRowCount", QString::number(m));
        stream.writeAttribute("x:FullColumns", "1");

        for(int j = 0; j < m; j++) {
            stream.writeStartElement("Row");
            for(int i = 0; i < n; i++) {
                stream.writeStartElement("Cell");
                stream.writeStartElement("Data");
                stream.writeAttribute("ss:Type", "String");
                stream.writeCharacters(table.cellAt(j, i));
                stream.writeEndElement(); //Data
                stream.writeEndElement(); // Cell
            }
            stream.writeEndElement(); // Row
        }
        stream.writeEndElement(); // Table
        stream.writeEndElement(); // WorkSheet
        stream.writeEndElement(); // Workbook

        stream.writeEndDocument();

        file.close();
        

    }
}
